#include <iostream>
#include <string>
#include <vector>
#include <cstdlib>
#include <pthread.h>
#include <unistd.h>

// class threadData
// {
// public:
//     int Excute()
//     {
//         return x + y;
//     }
// public:
//     std::string name;
//     int x;
//     int y;
// };

// class threadResult
// {
// public:
//     std::string print()
//     {
//         return std::to_string(x) + " + " + std::to_string(y) + " = " + std::to_string(result);
//     }

// public:
//     int x;
//     int y;
//     int result;
// };

// void *threadRun(void* args)
// {
//     // std::string name = (const char*)args;   
//     // 这里必须要做两件事：
       // 1. 将参数转换为实际可用的类型
       // 2. 使用static_cast保证编译器会进行类型检查，直接使用强制转换变量类型可能会导致错误
//     threadData *td = static_cast<threadData*>(args);
//     threadResult *ret = new threadResult();
//     int cnt = 10;
//     while (cnt)
//     {
//         //既然可以使用pthread_create中最后一个参数的成员变量
//         //当然也可以使用最后一个参数作为对象时的成员函数了
//         std::cout << td->name << "run... " << ", cnt: " << cnt-- << std::endl;
//         ret->result = td->Excute();
//         ret->x = td->x;
//         ret->y = td->y;
//         break;
//     }
//     delete td;
//     return (void *)ret;
// }

std::string PrintToHex(pthread_t &tid)
{
    char buffer[64];
    snprintf(buffer, sizeof(buffer), "%lx", tid);
    // std::cout << "tid: " << tid << std::endl; 
    return buffer;
}

// int main()
// {
//     pthread_t tid;
//     threadData *td = new threadData();
//     td->name = "thread-1";
//     td->x = 10;
//     td->y = 20;

//     //给threadRun指向的这个函数传参时，传入的参数类型也可以是一个类指针
//     int n = pthread_create(&tid, nullptr, threadRun, td);
//     if (n != 0) 
//     {
//         std::cerr << "create thread error" << std::endl;
//         return 1;
//     }

//     // tid 是一个虚拟地址
//     std::string tid_str = PrintToHex(tid);
//     std::cout << "tid: " << tid_str << std::endl;
    
//     std::cout << "main thread join begin..." << std::endl;
//     //pthread_join是主线程来回收新线程的函数，
//     //总是希望主线程能最后结束
//     // 对于code这个指针变量的说明：
//     //  1. 是一个在内存中开了实际空间的变量
//     //  2. 只要申请之后传给pthread_join这个函数，就能接收在pthread_create的函数执行后的返回值
//     //  3. 在pthread_join这个函数中，第二个参数是一个二级指针，是作为输出型参数，
//     //  让pthread_join修改之后，外部主线程能够看到这种修改
//     threadResult *ret = nullptr;
//     n = pthread_join(tid, (void **)&ret);
//     if (n == 0)
//     {
//         //这里想要打印code中的值，直接将code这个地址的值强转成64位的整数，之后才能打印  
//         std::cout << "main thread wait success, new thread exit code: " << ret->print() << std::endl;
//     }
//     return 0;
// }


// 下面展示创建多线程的步骤
const int num = 10;

void *threadRun(void *args)
{
    pthread_detach(pthread_self());
    std::string name = static_cast<const char*>(args);
    while (true)
    {
        std::cout << name << "is running" << std::endl;
        sleep(1);
        break;
    }
    // return args;
    // exit(1);
    pthread_exit(args);
}

int main()
{
    std::vector<pthread_t> tids;
    for (int i = 0; i < num; i++)
    {
        //  1. 每次进入线程，就创建一个线程id
        pthread_t tid;

        // 这里实现的功能是：每次进入循环，就会利用snprintf函数给这个name这个变量写入一串字符
        // 注意：
        //   如果写成char name[128], 此时就是把name 申请的空间放在了栈上，那么所有线程都会看到同一块栈上的空间
        //   这里只能new一块空间，将name的空间放到堆上，不同的线程看到不同的堆上的空间
        //   就会发生重设，给到threadRun的参数的内容就是未知的
        //  给定每个线程要执行函数的参数
        char *name = new char[128];
        snprintf(name, 128, "thread-%d", i + 1);
        pthread_create(&tid, nullptr, threadRun, name);
        tids.emplace_back(tid);
    }

    sleep(5);

    // 想要回收线程资源，有两种方式
    // 方式一：可以在for循环的代码块中利用pthread_join对所申请的所有线程依次进行资源的回收 和 返回值的获取
    // 方式二：可以在for循环的代码块中pthread_join对
    for(auto tid : tids)
    {
        pthread_cancel(tid);
        std::cout << "cancel: " << PrintToHex(tid) << std::endl;
        void *result = nullptr;
        pthread_join(tid, nullptr);

        std::cout << (long long int)result << " quit ..." << std::endl;
    }

    sleep(100);
    return 0;
}